package cn.signalpha.modules.com.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.signalpha.common.annotation.OptimisticLockRetryAnnotation;
import cn.signalpha.common.constant.CommonConstant;
import cn.signalpha.common.exception.OptimisticLockRetryException;
import cn.signalpha.common.utils.Arith;
import cn.signalpha.core.log.exception.ServiceException;
import cn.signalpha.core.tool.node.ForestNodeMerger;
import cn.signalpha.core.tool.utils.*;
import cn.signalpha.modules.auth.dto.UserInfo;
import cn.signalpha.modules.com.dto.UserLevelLogDTO;
import cn.signalpha.modules.com.dto.WalletDTO;
import cn.signalpha.modules.com.entity.*;
import cn.signalpha.modules.com.entity.enums.WalletAmountTypeEnum;
import cn.signalpha.modules.com.entity.enums.WalletTypeEnum;
import cn.signalpha.modules.com.excel.UserExcel;
import cn.signalpha.modules.com.excel.UserPlaceExcel;
import cn.signalpha.modules.com.mapper.UserMapper;
import cn.signalpha.modules.com.vo.UserPlaceVO;
import cn.signalpha.modules.com.vo.UserTreeVO;
import cn.signalpha.modules.com.vo.UserVO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static cn.signalpha.common.cache.CacheNames.EXPIRATION_TIME_7;
import static cn.signalpha.common.cache.CacheNames.USER;

/**
 * 服务类
 *
 * @author signalpha
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserService extends ServiceImpl<UserMapper, User> {
	private static final String USER_NAME = "user";
	private static final String GUEST_NAME = "guest";

	private final UserLevelService userLevelService;
	private final UserOauthService userOauthService;
	private final UserWalletService userWalletService;
	private final UserLevelLogService userLevelLogService;

	/**
	 * 获取基础信息
	 */
	@Cacheable(cacheNames = USER + EXPIRATION_TIME_7, key = "#id")
	public User getBaseById(Long id) {
		return baseMapper.selectById(id);
	}
	@CacheEvict(cacheNames = USER, key = "#id")
	public void removeBaseById(Long id) {
	}

	/**
	 * 自定义分页
	 */
    public IPage<UserVO> selectUserPage(IPage<UserVO> page, Map<String, Object> params) {
        return page.setRecords(baseMapper.selectUserPage(page, params));
    }

	/**
	 * excel导出
	 */
	public List<UserExcel> selectUserExcel(Map<String, Object> params) {
		return BeanUtil.copy(baseMapper.selectUserPage(null, params), UserExcel.class);
	}

	/**
	 * 分销员
	 */
	public IPage<UserPlaceVO> selectUserPlacePage(IPage<UserPlaceVO> page, Map<String, Object> params) {
		return page.setRecords(baseMapper.selectUserPlacePage(page, params));
	}

	/**
	 * 分销员excel导出
	 */
	public List<UserPlaceExcel> selectUserPlaceExcel(Map<String, Object> params) {
		return BeanUtil.copy(baseMapper.selectUserPlacePage(null, params), UserPlaceExcel.class);
	}

	/**
	 * 用户树
	 */
	public List<UserTreeVO> lazyTree(Long parentId) {
		return ForestNodeMerger.merge(baseMapper.lazyTree(parentId));
	}

	/**
	 * 自定义Wrapper
	 */
	public QueryWrapper<User> selectUserWrapper(Map<String, Object> params) {
		QueryWrapper<User> wrapper = Wrappers.query();
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("id")), User::getId, params.get("id"));
		wrapper.lambda().in(ObjectUtil.isNotEmpty(params.get("ids")), User::getId, Func.toLongList(String.valueOf(params.get("ids"))));
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("parentId")), User::getParentId, params.get("parentId"));
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("grandpaId")), User::getGrandpaId, params.get("grandpaId"));
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("status")), User::getStatus, params.get("status"));
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("level")), User::getLevel, params.get("level"));
		wrapper.lambda().eq(ObjectUtil.isNotEmpty(params.get("placeLevel")), User::getPlaceLevel, params.get("placeLevel"));
		wrapper.lambda().like(ObjectUtil.isNotEmpty(params.get("nickName")), User::getNickName, params.get("nickName"));
//		wrapper.lambda().ge(ObjectUtil.isNotEmpty(params.get("createTimeBegin")), User::getCreateTime, params.get("createTimeBegin"));
//		wrapper.lambda().le(ObjectUtil.isNotEmpty(params.get("createTimeEnd")), User::getCreateTime, params.get("createTimeEnd"));
		wrapper.lambda().ge(ObjectUtil.isNotEmpty(params.get("bindingTimeBegin")), User::getBindingTime, params.get("bindingTimeBegin"));
		wrapper.lambda().le(ObjectUtil.isNotEmpty(params.get("bindingTimeEnd")), User::getBindingTime, params.get("bindingTimeEnd"));
		wrapper.lambda().orderByDesc(User::getId);
		return wrapper;
	}

	/**
	 * 数量统计
	 */
	public long countByParams(Map<String, Object> params) {
		return count(selectUserWrapper(params));
	}

	/**
	 * 按日期统计
	 */
	public long countByCreateTime(Date beginDay, Date endDay) {
		QueryWrapper<User> wrapper = Wrappers.query();
		wrapper.lambda().ge(User::getCreateTime, beginDay);
		wrapper.lambda().le(User::getCreateTime, endDay);
		return baseMapper.selectCount(wrapper);
	}

	/**
	 * 手机号查找
	 */
	public User getByMobile(String mobile) {
		return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile));
	}

	/**
	 * 账号查找
	 */
	public User getByAccount(String account) {
		return baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getAccount, account));
	}

	/**
	 * 手机号查找
	 */
	public boolean countByMobile(String mobile) {
		return baseMapper.selectCount(Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile)) > 0;
	}

	/**
	 * 账号查找
	 */
	public boolean countByAccount(String account) {
		return baseMapper.selectCount(Wrappers.<User>lambdaQuery().eq(User::getAccount, account)) > 0;
	}

	/**
	 * 查找用户的直推
	 */
	public List<User> listDirectPush(Long userId) {
		return baseMapper.selectList(Wrappers.<User>lambdaQuery().eq(User::getParentId, userId));
	}

	/**
	 * 查找用户的直推id
	 */
	public List<Long> listDirectPushId(Long userId) {
		return listDirectPush(userId).stream().map(User::getId).collect(Collectors.toList());
	}

	/**
	 * 查找用户的团队 无线下级
	 */
	public List<User> listGroup(Long userId) {
		User user = getById(userId);
		return baseMapper.selectList(Wrappers.<User>lambdaQuery().likeRight(User::getChain, user.getChain() + "," + user.getId()));
	}

	/**
	 * 查找用户的团队id 无线下级
	 */
	public List<Long> listGroupId(Long userId) {
		return listGroup(userId).stream().map(User::getId).collect(Collectors.toList());
	}

	/**
	 * 用户信息
	 */
	public UserInfo userInfo(String username, String password) {
		return buildUserInfo(baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getMobile, username).eq(User::getLoginPwd, password)));
	}

	/**
	 * 用户信息
	 */
	public UserInfo userInfo(String mobile) {
		return buildUserInfo(baseMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile)));
	}

	/**
	 * 用户信息
	 */
	public UserInfo userInfo(Long userId) {
		return buildUserInfo(baseMapper.selectById(userId));
	}

	/**
	 * 用户信息
	 */
	public UserInfo userInfo(UserOauth userOauth) {
		UserInfo userInfo;
		UserOauth uo = userOauthService.getByOpenIdAndSource(userOauth.getOpenId(), userOauth.getSource());
		if (ObjectUtil.isNotEmpty(uo) && ObjectUtil.isNotEmpty(uo.getUserId())) {
			//补充unionId
			if(Func.isBlank(uo.getUnionId()) && Func.isNotBlank(userOauth.getUnionId())){
				userOauthService.updateById(UserOauth.builder().id(uo.getId()).unionId(userOauth.getUnionId()).build());
				userOauthService.removeCaCheByOpenIdAndSource(userOauth.getOpenId(), userOauth.getSource());
			}
			//如果是弹窗授权，把用户头像和昵称更新到用户信息里
			if(Func.isAnyBlank(uo.getAvatar(), uo.getNickName()) && Func.isNoneBlank(userOauth.getAvatar(), userOauth.getNickName())){
				userOauthService.updateById(UserOauth.builder().id(uo.getId()).avatar(userOauth.getAvatar()).nickName(userOauth.getNickName()).build());
				userOauthService.removeCaCheByOpenIdAndSource(userOauth.getOpenId(), userOauth.getSource());
				updateById(User.builder().id(uo.getUserId()).avatar(userOauth.getAvatar()).nickName(userOauth.getNickName()).build());
			}
			userInfo = this.userInfo(uo.getUserId());
			userInfo.setOauthId(Func.toStr(uo.getId()));
			return userInfo;
		}

		//公众号、小程序需要二次查询unionId
		if(StringUtil.isNotBlank(userOauth.getUnionId())){
			List<UserOauth> userOauthList = userOauthService.listByUnionId(userOauth.getUnionId());
			if(ObjectUtil.isNotEmpty(userOauthList)){
				userOauth.setUserId(userOauthList.get(0).getUserId());
				userOauth.setCreateTime(DateUtil.date());
				userOauthService.save(userOauth);
				userOauthService.removeCaCheByOpenIdAndSource(userOauth.getOpenId(), userOauth.getSource());
				userInfo = this.userInfo(userOauth.getUserId());
				userInfo.setOauthId(Func.toStr(userOauth.getId()));
				return userInfo;
			}
		}

		//入库
		User user = createUser(Objects.requireNonNull(BeanUtil.copy(userOauth, User.class)), StringPool.EMPTY);
		userOauth.setUserId(user.getId());
		userOauth.setCreateTime(DateUtil.date());
		userOauthService.save(userOauth);
		userOauthService.removeCaCheByOpenIdAndSource(userOauth.getOpenId(), userOauth.getSource());
		userInfo = buildUserInfo(user);
		assert userInfo != null;
		userInfo.setOauthId(Func.toStr(userOauth.getId()));
		return userInfo;
	}

	/**
	 * 构建用户信息
	 */
	private UserInfo buildUserInfo(User user) {
		if (ObjectUtil.isEmpty(user)) {
			return null;
		}

		UserInfo userInfo = Objects.requireNonNull(BeanUtil.copy(user, UserInfo.class));
		if(ObjectUtil.isEmpty(userInfo.getRoles())){
			userInfo.setRoles(Collections.singletonList(USER_NAME));
		}
		userInfo.setAccount(user.getMobile());
		return userInfo;
	}

	/**
	 * 重置用户密码
	 */
	public boolean resetPassword(String userIds) {
		User user = new User();
		user.setLoginPwd(DigestUtil.encrypt(CommonConstant.DEFAULT_PASSWORD));
		user.setUpdateTime(DateUtil.date());
		return update(user, Wrappers.<User>update().lambda().in(User::getId, Func.toLongList(userIds)));
	}

	/**
	 * 更新用户余额
	 */
	@OptimisticLockRetryAnnotation
	public boolean updateUserMoney(WalletDTO walletDTO) {
		User user = getById(walletDTO.getUserId());
		if(ObjectUtil.isEmpty(user)) {return false;}

		Double money = user.getMoney();
		if(walletDTO.getType().equals(WalletTypeEnum.ADD.getType())){
			money = Arith.add(money, walletDTO.getAmount());
		}else if(walletDTO.getType().equals(WalletTypeEnum.SUB.getType())){
			money = Arith.sub(money, walletDTO.getAmount());
			if(money < 0){
				throw new ServiceException("余额不足");
			}
		}else{
			throw new ServiceException("计算类型不正确");
		}

		Date dateTime = DateUtil.date();
		if(updateById(User.builder().id(user.getId()).money(money).updateTime(dateTime).version(user.getVersion()).build())){
			UserWallet userWallet = Objects.requireNonNull(BeanUtil.copy(walletDTO, UserWallet.class));
			userWallet.setAmountType(WalletAmountTypeEnum.MONEY.getType());
			userWallet.setAmountLast(money);
			userWallet.setCreateTime(dateTime);
			userWalletService.save(userWallet);
		}else{
			log.error("id:[{}]更新余额失败", user.getId());
			throw new OptimisticLockRetryException();
		}

		return true;
	}

	/**
	 * 更新用户积分
	 */
	@OptimisticLockRetryAnnotation
	public boolean updateUserIntegral(WalletDTO walletDTO) {
		User user = getById(walletDTO.getUserId());
		if(ObjectUtil.isEmpty(user)) {return false;}

		Double integral = user.getIntegral();
		if(walletDTO.getType().equals(WalletTypeEnum.ADD.getType())){
			integral = Arith.add(integral, walletDTO.getAmount());
		}else if(walletDTO.getType().equals(WalletTypeEnum.SUB.getType())){
			integral = Arith.sub(integral, walletDTO.getAmount());
			if(integral < 0){
				throw new ServiceException("积分不足");
			}
		}else{
			throw new ServiceException("计算类型不正确");
		}

		Date dateTime = DateUtil.date();
		if(updateById(User.builder().id(user.getId()).integral(integral).updateTime(dateTime).version(user.getVersion()).build())){
			UserWallet userWallet = Objects.requireNonNull(BeanUtil.copy(walletDTO, UserWallet.class));
			userWallet.setAmountType(WalletAmountTypeEnum.INTEGRAL.getType());
			userWallet.setAmountLast(integral);
			userWallet.setCreateTime(dateTime);
			userWalletService.save(userWallet);
		}else{
			log.error("id:[{}]更新积分失败", user.getId());
			throw new OptimisticLockRetryException();
		}

		return true;
	}

	/**
	 * 更新用户经验值
	 */
	@Transactional(rollbackFor = Exception.class)
	public void updateUserExp(UserLevelLogDTO userLevelLogDTO) {
		User user = baseMapper.selectById(userLevelLogDTO.getUserId());
		if(ObjectUtil.isEmpty(user)) {return ;}
		if(userLevelLogDTO.getAmount() <= 0){return ;}
		int extTotal = user.getExp() + userLevelLogDTO.getAmount();
		UserLevel userLevel = userLevelService.getById(user.getLevel() + 1);
		if(ObjectUtil.isEmpty(userLevel)){
			updateById(User.builder().id(user.getId()).exp(extTotal).build());
		}else{
			if(extTotal >= userLevel.getExpUp()){
				updateById(User.builder().id(user.getId()).level(userLevel.getId()).exp(extTotal).build());
			}else{
				updateById(User.builder().id(user.getId()).exp(extTotal).build());
			}
		}

		UserLevelLog userLevelLog = Objects.requireNonNull(BeanUtil.copy(userLevelLogDTO, UserLevelLog.class));
		userLevelLog.setCreateTime(new Date());
		userLevelLogService.save(userLevelLog);
	}

	/**
	 * 创建用户
	 */
	@Transactional(rollbackFor = Exception.class)
	public User createUser(User user, String shareCode) {
		Date dateTime = new Date();

		user.setParentId(0L);
		user.setGrandpaId(0L);
		user.setLevel(0);
		user.setChain("0");
		user.setCreateTime(dateTime);
		user.setUpdateTime(dateTime);
		user.setAccount(RandomUtil.randomStringUpper(7));

//		if(Func.isBlank(user.getAvatar())){
//			user.setAvatar(ParamCache.getValue(ParamKeys.DEFAULT_AVATAR));
//		}
//		if(Func.isBlank(user.getNickName())){
//			user.setNickName("用户".concat(RandomUtil.randomNumbers(10)));
//		}
		if(StringUtil.isNotBlank(user.getLoginPwd())){
   			user.setLoginPwd(DigestUtil.encrypt(user.getLoginPwd()));
		}
		if(StringUtil.isNotBlank(shareCode)){
			User referrer = getByAccount(shareCode);
			if(Func.isNotEmpty(referrer)){
				user.setParentId(referrer.getId());
				user.setGrandpaId(referrer.getParentId());
				user.setChain(referrer.getChain() + "," + user.getParentId());
			}else {
				throw new ServiceException("无效的推荐信息");
			}
		}

		if(StringUtil.isNotBlank(user.getMobile())){
			if(countByMobile(user.getMobile())){
				throw new ServiceException(StringUtil.format("当前手机号 [{}] 已注册", user.getMobile()));
			}
		}

		baseMapper.insert(user);
		return user;
	}

	/**
	 * 修改推荐人
	 */
	@Transactional(rollbackFor = Exception.class)
	public boolean updateParentId(List<Long> ids, Long parentId) {
		User user = getById(parentId);
		for(Long id : ids){
			updateById(User.builder().id(id).parentId(user.getId()).grandpaId(user.getParentId()).chain(user.getChain() + "," + user.getId()).build());
			updateParentId(baseMapper.selectList(Wrappers.<User>lambdaQuery().eq(User::getParentId, id)).stream().map(User::getId).collect(Collectors.toList()), id);
		}
		return true;
	}

}
